#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <libgen.h>  // 用于 dirname() 处理目录路径

/**
 * @brief 打印程序使用帮助
 */
void print_help(const char *prog_name) {
    fprintf(stdout, "软链接创建工具（softpath）- 不依赖外部 ln 程序\n");
    fprintf(stdout, "用法：%s [选项] <源文件路径> <目标软链接路径>\n", prog_name);
    fprintf(stdout, "选项：\n");
    fprintf(stdout, "  -f, --force   强制覆盖已存在的目标路径（默认不覆盖）\n");
    fprintf(stdout, "  -h, --help    显示此帮助信息\n");
    fprintf(stdout, "\n示例：\n");
    fprintf(stdout, "  1. 创建软链接：%s ~/Download/go/bin/go /usr/local/bin/go\n", prog_name);
    fprintf(stdout, "  2. 强制覆盖已存在链接：%s -f ~/Download/go/bin/go /usr/local/bin/go\n", prog_name);
}

/**
 * @brief 检查源文件是否存在且为普通文件/目录
 * @param src_path 源文件路径
 * @return 0：源文件有效；-1：源文件无效（输出错误原因）
 */
int check_source_valid(const char *src_path) {
    struct stat src_stat;

    // 检查源文件是否存在
    if (stat(src_path, &src_stat) == -1) {
        fprintf(stderr, "错误：源路径不存在或无法访问 -> %s\n", src_path);
        fprintf(stderr, "      原因：%s\n", strerror(errno));
        return -1;
    }

    // 允许源文件为普通文件或目录（软链接可指向两者）
    if (!S_ISREG(src_stat.st_mode) && !S_ISDIR(src_stat.st_mode)) {
        fprintf(stderr, "错误：源路径不是普通文件或目录 -> %s\n", src_path);
        fprintf(stderr, "      类型：%s\n", S_ISLNK(src_stat.st_mode) ? "软链接" : 
                                       S_ISCHR(src_stat.st_mode) ? "字符设备" : 
                                       S_ISBLK(src_stat.st_mode) ? "块设备" : "其他特殊文件");
        return -1;
    }

    return 0;
}

/**
 * @brief 检查目标路径是否存在，处理冲突（支持强制覆盖）
 * @param dest_path 目标软链接路径
 * @param force 是否强制覆盖（1：强制；0：不强制）
 * @return 0：目标路径可创建；-1：目标路径冲突且不强制覆盖
 */
int check_dest_conflict(const char *dest_path, int force) {
    struct stat dest_stat;

    // 检查目标路径是否存在
    if (stat(dest_path, &dest_stat) == 0) {
        // 目标路径已存在：判断是否为软链接（避免误删真实文件）
        if (S_ISLNK(dest_stat.st_mode)) {
            char existing_src[1024];
            ssize_t len = readlink(dest_path, existing_src, sizeof(existing_src) - 1);
            if (len != -1) {
                existing_src[len] = '\0';
                fprintf(stdout, "提示：目标软链接已存在 -> %s\n", dest_path);
                fprintf(stdout, "      当前指向：%s\n", existing_src);
            } else {
                fprintf(stdout, "提示：目标路径已存在（无法读取软链接指向）-> %s\n", dest_path);
            }
        } else {
            fprintf(stderr, "警告：目标路径已存在且不是软链接 -> %s\n", dest_path);
            fprintf(stderr, "      类型：%s（强制覆盖可能导致数据丢失！）\n", 
                    S_ISREG(dest_stat.st_mode) ? "普通文件" : "目录");
        }

        // 若不强制覆盖，提示用户确认
        if (!force) {
            char reply;
            fprintf(stdout, "是否覆盖？(y/N) ");
            if (scanf("%c", &reply) != 1 || (reply != 'y' && reply != 'Y')) {
                fprintf(stdout, "操作取消：用户拒绝覆盖目标路径\n");
                return -1;
            }
        }

        // 强制覆盖：删除已存在的目标路径
        if (unlink(dest_path) == -1) {
            fprintf(stderr, "错误：无法删除已存在的目标路径 -> %s\n", dest_path);
            fprintf(stderr, "      原因：%s（可能缺少权限）\n", strerror(errno));
            return -1;
        }
        fprintf(stdout, "已删除已存在的目标路径：%s\n", dest_path);
    } else if (errno != ENOENT) {
        // stat 失败且不是“文件不存在”（如权限不足）
        fprintf(stderr, "错误：无法检查目标路径状态 -> %s\n", dest_path);
        fprintf(stderr, "      原因：%s\n", strerror(errno));
        return -1;
    }

    return 0;
}

/**
 * @brief 确保目标路径的父目录存在（如 /usr/local/bin 不存在则创建）
 * @param dest_path 目标软链接路径
 * @return 0：父目录存在或创建成功；-1：创建父目录失败
 */
int ensure_parent_dir(const char *dest_path) {
    char *dest_copy = strdup(dest_path);
    if (dest_copy == NULL) {
        fprintf(stderr, "错误：内存分配失败（strdup）\n");
        return -1;
    }

    // 获取目标路径的父目录（如 /usr/local/bin/go -> /usr/local/bin）
    char *parent_dir = dirname(dest_copy);
    if (parent_dir == NULL || strcmp(parent_dir, ".") == 0) {
        free(dest_copy);
        return 0;  // 父目录是当前目录，无需创建
    }

    // 检查父目录是否存在，不存在则创建（递归创建多级目录）
    struct stat dir_stat;
    if (stat(parent_dir, &dir_stat) == -1) {
        if (mkdir(parent_dir, 0755) == -1) {
            fprintf(stderr, "错误：无法创建父目录 -> %s\n", parent_dir);
            fprintf(stderr, "      原因：%s（可能缺少权限，需 sudo 运行）\n", strerror(errno));
            free(dest_copy);
            return -1;
        }
        fprintf(stdout, "已创建父目录：%s（权限：0755）\n", parent_dir);
    } else if (!S_ISDIR(dir_stat.st_mode)) {
        fprintf(stderr, "错误：父路径不是目录 -> %s\n", parent_dir);
        free(dest_copy);
        return -1;
    }

    free(dest_copy);
    return 0;
}

/**
 * @brief 创建软链接（核心函数）
 * @param src_path 源文件路径
 * @param dest_path 目标软链接路径
 * @param force 是否强制覆盖已存在目标
 * @return 0：创建成功；-1：创建失败
 */
int create_symlink(const char *src_path, const char *dest_path, int force) {
    // 步骤1：检查源文件有效性
    if (check_source_valid(src_path) != 0) {
        return -1;
    }

    // 步骤2：确保目标路径的父目录存在
    if (ensure_parent_dir(dest_path) != 0) {
        return -1;
    }

    // 步骤3：处理目标路径冲突
    if (check_dest_conflict(dest_path, force) != 0) {
        return -1;
    }

    // 步骤4：调用系统函数创建软链接
    if (symlink(src_path, dest_path) == -1) {
        fprintf(stderr, "错误：创建软链接失败！\n");
        fprintf(stderr, "      源：%s\n", src_path);
        fprintf(stderr, "      目标：%s\n", dest_path);
        fprintf(stderr, "      原因：%s（若目标在 /usr/local/bin 等系统目录，需 sudo 运行）\n", strerror(errno));
        return -1;
    }

    // 步骤5：验证软链接是否创建成功
    char verify_src[1024];
    ssize_t len = readlink(dest_path, verify_src, sizeof(verify_src) - 1);
    if (len != -1) {
        verify_src[len] = '\0';
        fprintf(stdout, "\n✅ 软链接创建成功！\n");
        fprintf(stdout, "   源文件：%s\n", src_path);
        fprintf(stdout, "   软链接：%s -> %s\n", dest_path, verify_src);
    } else {
        fprintf(stdout, "\n✅ 软链接创建成功（无法验证指向，可能权限不足）！\n");
        fprintf(stdout, "   源文件：%s\n", src_path);
        fprintf(stdout, "   软链接：%s\n", dest_path);
    }

    return 0;
}

int main(int argc, char *argv[]) {
    int force = 0;          // 是否强制覆盖
    const char *src = NULL; // 源文件路径
    const char *dest = NULL;// 目标软链接路径

    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_help(argv[0]);
            return EXIT_SUCCESS;
        } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--force") == 0) {
            force = 1;
        } else if (src == NULL) {
            src = argv[i];
        } else if (dest == NULL) {
            dest = argv[i];
        } else {
            fprintf(stderr, "错误：多余的参数 -> %s\n", argv[i]);
            fprintf(stderr, "      使用 %s -h 查看正确用法\n", argv[0]);
            return EXIT_FAILURE;
        }
    }

    // 检查必要参数是否缺失
    if (src == NULL || dest == NULL) {
        fprintf(stderr, "错误：缺少必要参数（源路径或目标路径）\n");
        fprintf(stderr, "      使用 %s -h 查看正确用法\n", argv[0]);
        return EXIT_FAILURE;
    }

    // 调用核心函数创建软链接
    if (create_symlink(src, dest, force) != 0) {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
